#include "../include/Table.h"

Table::Table()
{
    AttributeName = new string[MAX];
    AttributeType = new int[MAX];
    AttributeSize = new int[MAX];
    IsPrimary = new bool[MAX];
    TableData = new vector<void*>[MAX];
}
Table::Table(Create_Stmt CreateData)
{
    AttributeName = new string[MAX];
    AttributeType = new int[MAX];
    AttributeSize = new int[MAX];
    IsPrimary = new bool[MAX];
    TableData = new vector<void*>[MAX];
    TableName = CreateData.table;
    list<Columndef_Stmt*>::iterator it;
    int i ;
    for(it = CreateData.Column.begin(), i=0; it != CreateData.Column.end(); it++,i++)
    {
        /*
        if((*it)->ColumnType != 0 && (*it)->ColumnType != 1)
        {
            cerr<<(*it)->ColumnType<<" : Data Type doesn't supported."<<endl;
            exit(0);
        }*/
        AttributeName[i] =(*it)->Name;
        AttributeType[i] = (*it)->ColumnType;
        AttributeSize[i] = (*it)->Size;
        IsPrimary[i] = (*it)->IsPrimary;
    }
    TableSize = i;
};

void Table::Insert(Insert_Stmt InsertData)
{
    list<string>::iterator it1;
    list<Sub_Stmt*>::iterator it2;
    int i,j;
    int NameOrder[MAX] = {0,1,2,3,4};
    if(InsertData.AnyName)
    {
        for(it1 = InsertData.Name.Columnname.begin(),i=0; it1 != InsertData.Name.Columnname.end(); it1++,i++)
        {

            for(j = 0; j<TableSize; j++)
            {
                if(AttributeName[j] == *it1)
                {
                    int n= NameOrder[i];
                    NameOrder[i]=NameOrder[j];
                    NameOrder[j]=n;
                    break;
                }
            }
            if(j == TableSize) // consider the attribute input is wrong.
            {
                cerr<<"the attribute : "<<*it1<<" you want to insert is not exist."<<endl;
                exit(0);
            }
        }
        for(; i<TableSize; i++)
            NameOrder[i] = -1 - NameOrder[i];// set a flag that not inputed .負數
    }

    if(!CherkInsert(InsertData,NameOrder)) exit(0);
    //bool inserted[MAX]={};
    for(it2  = InsertData.Value.value.begin(), i=0;i<TableSize ; i++)//it2 != InsertData.Value.value.end()
    {
        j= NameOrder[i] ;
        //inserted[j] = true;
        if(j < 0)
            TableData[-1-j].push_back(NULL);// Sub_Stmt* is null; set 0 to represent null.
        else
        {
             if(AttributeType[i] == 0)
                    TableData[j].push_back( (void*)&(((String_Stmt*)*it2)->str));
             if(AttributeType[i] == 1)
                    TableData[j].push_back( (void*)&(((Number_Stmt*)*it2)->num));
            it2++;
        }
    }
}

void Table::Print(ofstream &Output)
{
    Output<<"Table Name :  "<<TableName<<endl;
    //Output<<"--------------------------------------------------"<<endl;
    //vector<void*>::iterator it[MAX];
    int i;
    Output<<"| ";
    for(i=0; i<TableSize; i++)
    {
        string atype;
        if(AttributeType[i]==0)
            atype="(VARCHAR)";
        else
            atype = "(INT)";
        AttributeName[i] =AttributeName[i]+atype;
        if(IsPrimary[i])
            AttributeName[i]="P_"+AttributeName[i];
        if(AttributeName[i].size() > (unsigned)AttributeSize[i])
            AttributeSize[i] =AttributeName[i].size();
        Output<<setw(AttributeSize[i])<<AttributeName[i]<<" | ";
    }
    Output<<endl;
    //Output<<"--------------------------------------------------"<<endl;
    //for(i=0; i<TableSize; i++)
        //it[i] = TableData[i].begin();
    //while(it[0]!=TableData[0].end())
    int ts = TableData[0].size();
    for(int j =0;j<ts;j++)
    {
        Output<<"| ";
        for(i=0; i<TableSize; i++)
        {
            if(TableData[i][j] == NULL)
            {
                Output<<setw(AttributeSize[i])<<"NULL"<<" | ";//null / not input
            }
            else
            {

            //    if(i==1)

                if(AttributeType[i] == 0)
                    Output<<setw(AttributeSize[i])<< *((string*)TableData[i][j])<<" | ";
                if(AttributeType[i] == 1)
                    Output<<setw(AttributeSize[i])<< *((int*)TableData[i][j])<<" | ";
            //(it[i])++;
            }
        }
        Output<<endl;
    }
    Output<<endl;
};
/*
void writestring(string &str, FILE *fp)
{
    int n = str.size();
    fwrite(&n,IBYTES,1,fp);
    fwrite(str.c_str(),n,1,fp);
};
string* readstring(FILE *fp)
{
    int n;
    fread(&n,IBYTES,1,fp);
    char* cstr = new char[n+1];
    fread(cstr,n,1,fp);
    cstr[n]='\0';
    string* str= new string(cstr);
    delete cstr;
    return str;//str = cstr;//錯了

};*/

void writestring(string &str, ofstream &out)
{
    int n = str.size()+1;
    out.write((char*)&n,IBYTES);
    out.write(str.c_str(),n);
};
string* readstring(ifstream &in)
{
    int n;
    in.read((char*)&n,IBYTES);
    char* cstr = new char[n];
    in.read(cstr,n);
    //cstr[n]='\0';
    string* str= new string(cstr);
    delete cstr;
    return str;

};
/*
void Table::Write(FILE* fp)
{
    vector<void*>::iterator it;

    writestring(TableName,fp);
    fwrite(&TableSize,IBYTES,1,fp);
    for(int i=0;i<TableSize;i++)
    {
        writestring(AttributeName[i],fp);
        fwrite(&AttributeType[i],IBYTES,1,fp);
        fwrite(&AttributeSize[i],IBYTES,1,fp);
        fwrite(&IsPrimary[i],IBYTES,1,fp);
        int ll= TableData[i].size();
        fwrite(&ll,IBYTES,1,fp);
        if(AttributeType[i]==0)
        {
            for(it = TableData[i].begin() ;it!=TableData[i].end();it++ )
                writestring(*((string*)*it),fp);
        }
        else
        {
            for(it = TableData[i].begin() ;it!=TableData[i].end();it++ )
                fwrite(*it,IBYTES,1,fp);
        }
    }
};

void Table::Read(FILE* fp)
{
        TableName=*readstring(fp);
        fread(&TableSize,IBYTES,1,fp);
        for(int i=0;i<TableSize;i++)
        {
            AttributeName[i]=*readstring(fp);
            fread(&AttributeType[i],IBYTES,1,fp);
            fread(&AttributeSize[i],IBYTES,1,fp);
            fread(&AttributeSize[i],IBYTES,1,fp);
            fread(&IsPrimary[i],IBYTES,1,fp);
            int ll=0;
            fread(&ll,IBYTES,1,fp);
            if(AttributeType[i]==0)
            {
                for(int j=0;j<ll;j++)
                    //string tmp (readstring(fin));
                    TableData[i].push_back((void*)readstring(fp));
            }
            else
            {
                 for(int j=0;j<ll;j++)
                {
                    int* tmp = new int;
                    fread(&tmp,IBYTES,1,fp);
                    TableData[i].push_back((void*)tmp);
                }
            }
        }

};
*/
void Table::Write(ofstream &Output)
{
    //vector<void*>::iterator it;
    writestring(TableName,Output);
    Output.write((char*)&TableSize,IBYTES);
    int dlen = TableData[0].size();
    Output.write((char*)&dlen,IBYTES);
    for(int i=0;i<TableSize;i++)
    {
        //int a;cin>>a;

        writestring(AttributeName[i],Output);
        Output.write((char*)&AttributeType[i],IBYTES);
         Output.write((char*)&AttributeSize[i],IBYTES);
         Output.write((char*)&IsPrimary[i],IBYTES);
           int ma =0;
            int mi =0;
        if(AttributeType[i]==0)
        {
          int all=0;
           int* intarray = new int[dlen];
            for(int j =0 ;j<dlen;j++)
                {intarray[j] = (*(string*)TableData[i][j]).size() +1;
                  all = all+ intarray[j];
                }
            Output.write((char*)intarray,IBYTES*dlen);
            Output.write((char*)&all,IBYTES);
            for(int j =0 ;j<dlen;j++)
                Output.write((*(string*)TableData[i][j]).c_str(),intarray[j]);
            //writestringCPP(*((string*)*it),Output);
        }
        else
          {
            if(dlen>0)
            {
            void* val0 = TableData[i][0];
            ma = *((int*)val0);
            mi = *((int*)val0);
            }
            int* intarray = new int[dlen];
            for(int j =0 ;j<dlen;j++)
            {
              intarray[j]=(*((int*)TableData[i][j]));

                  if(intarray[j]>ma)
                    ma = intarray[j];
                  if(intarray[j]<mi)
                    mi = intarray[j];
            }
            Output.write((char*)intarray,IBYTES*dlen);
        }
        Output.write((char*)&ma,IBYTES);
        Output.write((char*)&mi,IBYTES);
    }
};

void Table::Read(ifstream &fin)
{
        TableName=*readstring(fin);

        fin.read((char*)&TableSize,IBYTES);
        int dlen =0;
        fin.read((char*)&dlen,IBYTES);

        for(int i=0;i<TableSize;i++)
        {
             AttributeName[i]=*readstring(fin);
             fin.read((char*)&AttributeType[i],IBYTES);
             fin.read((char*)&AttributeSize[i],IBYTES);
             fin.read((char*)&IsPrimary[i],IBYTES);
            if(AttributeType[i]==0)
              {
                int* intarray = new int[dlen];
                fin.read((char*)intarray,IBYTES*dlen);
                int all=0;
                fin.read((char*)&all,IBYTES);
                char* stringarray = new char [all];
                fin.read((char*)stringarray,all);
                int offset=0; //-intarray[0];
                for(int j=0;j<dlen;j++)
                  {
                    string* tmp = new string(stringarray+offset);
                    //string tmp (readstring(fin));
                    TableData[i].push_back((void*)tmp);
                    offset = offset + intarray[j];
                  }
            }
            else
              {
                int* intarray = new int[dlen];
            fin.read((char*)intarray,IBYTES*dlen);
            for(int j=0; j<dlen; j++)
                TableData[i].push_back((void*)(intarray+j));
            }
        int ma=0 ,mi=0;
        fin.read((char*)&ma,IBYTES);
        fin.read((char*)&mi,IBYTES);

        maxb.push_back(ma);
        minb.push_back(mi);
    }

};

void Table::DeleteTable()
{
    delete [] AttributeName;
    delete [] AttributeType;
    delete [] AttributeSize;
    delete [] IsPrimary;
    delete [] TableData;
};

Table::~Table()
{
    /*delete [] AttributeName;
     delete [] AttributeType;
     delete [] AttributeSize;
     delete [] IsPrimary;
     delete [] TableData;*/
};

bool Table::CherkInsert(Insert_Stmt InsertData,int NameOrder[])
{
    list<Sub_Stmt*>::iterator it1;
    vector<void*>::iterator it2;
    int i,j;
    Sub_Stmt* temp =0 ;
    for(it1 = InsertData.Value.value.begin(), i=0; i<TableSize;i++)//it != InsertData.Value.value.end()
    {
        j = NameOrder[i];
        if(j >=0)
        {
            if(IsPrimary[j])
            {
            temp = *it1;
            break;
            }
            it1++;
        }
        else if(IsPrimary[-1-j])
        {
            cerr<<"primary key can not be null."<<endl;
            return false;
        }
    }
    if(temp)//consider not primary key table.
    {
        if(AttributeType[j] == 0)
        {
            for(it2 = TableData[j].begin(); it2!=TableData[j].end(); it2++)
            {
                if(*((string*)*it2) == ((String_Stmt*)temp)->str )
                {
                    cerr<<"primary key can not be repeated."<<endl;
                    return false;
                }
            }
        }
        if(AttributeType[j] == 1)
        {
            for(it2 = TableData[j].begin(); it2!=TableData[j].end(); it2++)
            {
                if(((Number_Stmt*)*it2)->num == ((Number_Stmt*)temp)->num )
                {
                    cerr<<"primary key can not be repeated."<<endl;
                    return false;
                }
            }
        }
    }
    for(it1 = InsertData.Value.value.begin(), i=0; it1 != InsertData.Value.value.end(); it1++,i++)
    {
        j= NameOrder[i];
        if((AttributeType[j] + (*it1)->type ) != 5 )
            //enum DataType{Varchar,Int} ;varchar = 0 ,int =1
            //enum{NAME,TABLE,FIELDNAME,ALIAS,NUMBER,STRING... ;NUMBER =4, STRING = 5
        {
            cerr<<"Data type invalidation."<<endl;
            return false;
        }
        if(AttributeType[j] == 0)
        {
            string str = ((String_Stmt*)*it1)->str ;

            if(str.size() > (unsigned)AttributeSize[j])
            {
                cerr<<"ColumnSize invalidation."<<endl;
                return false;
            }
        }
    }
    return true;
};
